<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>网络请求测试工具</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: '#165DFF',
            secondary: '#00B42A',
            danger: '#F53F3F',
            warning: '#FF7D00',
            dark: '#1D2129',
            light: '#F2F3F5'
          },
          fontFamily: {
            inter: ['Inter', 'system-ui', 'sans-serif'],
          },
        },
      }
    }
  </script>
  <style type="text/tailwindcss">
    @layer utilities {
      .content-auto {
        content-visibility: auto;
      }
      .scrollbar-thin {
        scrollbar-width: thin;
      }
      .scrollbar-none::-webkit-scrollbar {
        display: none;
      }
      .text-shadow {
        text-shadow: 0 2px 4px rgba(0,0,0,0.1);
      }
      .card-hover {
        @apply transition-all duration-300 hover:shadow-lg hover:-translate-y-1;
      }
    }
  </style>
</head>
<body class="font-inter bg-gray-50 text-dark min-h-screen flex flex-col">
  <!-- 顶部导航栏 -->
  <header class="bg-white shadow-sm sticky top-0 z-50">
    <div class="container mx-auto px-4 py-3 flex items-center justify-between">
      <div class="flex items-center space-x-2">
        <i class="fa fa-exchange text-primary text-2xl"></i>
        <h1 class="text-xl font-bold text-dark">网络请求测试工具</h1>
      </div>
      <div class="flex items-center space-x-4">
        <button id="theme-toggle" class="p-2 rounded-full hover:bg-gray-100 transition-colors">
          <i class="fa fa-moon-o text-gray-600"></i>
        </button>
        <button id="help-btn" class="p-2 rounded-full hover:bg-gray-100 transition-colors">
          <i class="fa fa-question-circle text-gray-600"></i>
        </button>
      </div>
    </div>
  </header>

  <!-- 主要内容区 -->
  <main class="flex-grow container mx-auto px-4 py-6">
    <!-- 请求配置区 -->
    <div class="bg-white rounded-xl shadow-md p-6 mb-6 card-hover">
      <div class="flex flex-col md:flex-row md:items-center mb-4 gap-4">
        <div class="flex space-x-2">
          <button class="method-btn bg-primary text-white px-4 py-2 rounded-lg font-medium" data-method="GET">GET</button>
          <button class="method-btn bg-white text-dark border border-gray-300 px-4 py-2 rounded-lg font-medium" data-method="POST">POST</button>
          <button class="method-btn bg-white text-dark border border-gray-300 px-4 py-2 rounded-lg font-medium" data-method="PUT">PUT</button>
          <button class="method-btn bg-white text-dark border border-gray-300 px-4 py-2 rounded-lg font-medium" data-method="DELETE">DELETE</button>
          <button class="method-btn bg-white text-dark border border-gray-300 px-4 py-2 rounded-lg font-medium" data-method="PATCH">PATCH</button>
        </div>
        <div class="flex-grow">
          <input id="url-input" type="text" 
            class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary outline-none transition-all"
            placeholder="https://api.example.com/data">
        </div>
        <button id="send-request" class="bg-secondary hover:bg-secondary/90 text-white px-6 py-2 rounded-lg font-medium transition-all flex items-center gap-2">
          <i class="fa fa-paper-plane"></i>
          <span>发送请求</span>
        </button>
      </div>

      <!-- 请求头配置 -->
      <div class="mb-4">
        <div class="flex items-center justify-between mb-2">
          <h3 class="font-medium text-gray-700">请求头</h3>
          <button id="add-header" class="text-primary hover:text-primary/80 text-sm font-medium flex items-center gap-1">
            <i class="fa fa-plus-circle"></i> 添加
          </button>
        </div>
        <div id="headers-container" class="space-y-2">
          <div class="flex items-center gap-2">
            <input type="text" placeholder="Key" value="Content-Type" class="header-key flex-grow max-w-[150px] px-3 py-2 border border-gray-300 rounded-lg text-sm">
            <input type="text" placeholder="Value" value="application/json" class="header-value flex-grow px-3 py-2 border border-gray-300 rounded-lg text-sm">
            <button class="remove-header text-gray-400 hover:text-danger">
              <i class="fa fa-times-circle"></i>
            </button>
          </div>
        </div>
      </div>

      <!-- 请求体配置 -->
      <div class="mb-4">
        <div class="flex items-center justify-between mb-2">
          <h3 class="font-medium text-gray-700">请求体</h3>
          <div class="flex space-x-2">
            <button class="body-type-btn bg-primary/10 text-primary px-3 py-1 rounded-md text-sm font-medium" data-type="raw">原始数据</button>
            <button class="body-type-btn bg-white text-gray-600 px-3 py-1 rounded-md text-sm font-medium" data-type="form">表单数据</button>
          </div>
        </div>
        <div id="raw-body-container" class="block">
          <textarea id="request-body" rows="5" 
            class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary/50 focus:border-primary outline-none transition-all text-sm"
            placeholder='{"key": "value"}'>{"name": "测试", "age": 25}</textarea>
        </div>
        <div id="form-body-container" class="hidden space-y-2">
          <div class="flex items-center gap-2">
            <input type="text" placeholder="Key" class="form-key flex-grow max-w-[150px] px-3 py-2 border border-gray-300 rounded-lg text-sm">
            <input type="text" placeholder="Value" class="form-value flex-grow px-3 py-2 border border-gray-300 rounded-lg text-sm">
            <button class="remove-form text-gray-400 hover:text-danger">
              <i class="fa fa-times-circle"></i>
            </button>
          </div>
          <button id="add-form" class="text-primary hover:text-primary/80 text-sm font-medium flex items-center gap-1 mt-2">
            <i class="fa fa-plus-circle"></i> 添加字段
          </button>
        </div>
      </div>

      <!-- 请求选项 -->
      <div class="flex flex-wrap gap-4 items-center mb-2">
        <div class="flex items-center gap-2">
          <input type="checkbox" id="follow-redirects" class="w-4 h-4 text-primary focus:ring-primary/50 border-gray-300 rounded">
          <label for="follow-redirects" class="text-sm text-gray-700">跟随重定向</label>
        </div>
        <div class="flex items-center gap-2">
          <input type="checkbox" id="with-credentials" class="w-4 h-4 text-primary focus:ring-primary/50 border-gray-300 rounded">
          <label for="with-credentials" class="text-sm text-gray-700">包含凭证</label>
        </div>
        <div class="flex items-center gap-2">
          <label for="timeout" class="text-sm text-gray-700">超时(ms):</label>
          <input type="number" id="timeout" value="0" min="0" 
            class="w-20 px-3 py-1 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary/50 focus:border-primary outline-none">
        </div>
      </div>
    </div>

    <!-- 请求历史 -->
    <div class="bg-white rounded-xl shadow-md p-6 mb-6 card-hover">
      <div class="flex items-center justify-between mb-4">
        <h2 class="text-lg font-semibold">请求历史</h2>
        <button id="clear-history" class="text-danger hover:text-danger/80 text-sm font-medium flex items-center gap-1">
          <i class="fa fa-trash"></i> 清空历史
        </button>
      </div>
      <div id="history-list" class="space-y-2 max-h-40 overflow-y-auto scrollbar-thin">
        <!-- 历史记录会动态添加到这里 -->
        <div class="text-center text-gray-500 text-sm italic py-4">暂无请求历史</div>
      </div>
    </div>

    <!-- 响应结果区 -->
    <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
      <!-- 响应状态 -->
      <div class="bg-white rounded-xl shadow-md p-6 card-hover">
        <h2 class="text-lg font-semibold mb-4">响应状态</h2>
        <div id="response-status" class="bg-light rounded-lg p-4 min-h-[150px]">
          <div class="flex flex-col justify-center items-center h-full text-gray-500">
            <i class="fa fa-info-circle text-3xl mb-2"></i>
            <p>发送请求后显示响应状态</p>
          </div>
        </div>
      </div>

      <!-- 响应头 -->
      <div class="bg-white rounded-xl shadow-md p-6 card-hover">
        <h2 class="text-lg font-semibold mb-4">响应头</h2>
        <div id="response-headers" class="bg-light rounded-lg p-4 min-h-[150px] overflow-auto max-h-[300px] scrollbar-thin">
          <div class="flex flex-col justify-center items-center h-full text-gray-500">
            <i class="fa fa-list-alt text-3xl mb-2"></i>
            <p>发送请求后显示响应头</p>
          </div>
        </div>
      </div>

      <!-- 响应时间 -->
      <div class="bg-white rounded-xl shadow-md p-6 card-hover">
        <h2 class="text-lg font-semibold mb-4">响应时间</h2>
        <div id="response-time" class="bg-light rounded-lg p-4 min-h-[150px]">
          <div class="flex flex-col justify-center items-center h-full text-gray-500">
            <i class="fa fa-clock-o text-3xl mb-2"></i>
            <p>发送请求后显示响应时间</p>
          </div>
        </div>
      </div>

      <!-- 响应体 -->
      <div class="lg:col-span-3 bg-white rounded-xl shadow-md p-6 card-hover">
        <div class="flex items-center justify-between mb-4">
          <h2 class="text-lg font-semibold">响应体</h2>
          <div class="flex space-x-2">
            <button class="response-view-btn bg-primary/10 text-primary px-3 py-1 rounded-md text-sm font-medium" data-view="preview">预览</button>
            <button class="response-view-btn bg-white text-gray-600 px-3 py-1 rounded-md text-sm font-medium" data-view="raw">原始</button>
            <button class="response-view-btn bg-white text-gray-600 px-3 py-1 rounded-md text-sm font-medium" data-view="formatted">格式化</button>
          </div>
        </div>
        <div id="response-body" class="bg-light rounded-lg p-4 min-h-[300px] overflow-auto max-h-[500px] scrollbar-thin">
          <div class="flex flex-col justify-center items-center h-full text-gray-500">
            <i class="fa fa-file-text-o text-4xl mb-2"></i>
            <p class="text-lg">发送请求后显示响应内容</p>
          </div>
        </div>
      </div>
    </div>
  </main>

  <!-- 页脚 -->
  <footer class="bg-white border-t border-gray-200 py-4 mt-6">
    <div class="container mx-auto px-4 text-center text-gray-500 text-sm">
      <p>网络请求测试工具 &copy; 2025 | 使用 HTML, Tailwind CSS 和 JavaScript 构建</p>
    </div>
  </footer>

  <!-- 帮助模态框 -->
  <div id="help-modal" class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center hidden">
    <div class="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
      <div class="p-6 border-b border-gray-200">
        <div class="flex items-center justify-between">
          <h3 class="text-xl font-bold">使用帮助</h3>
          <button id="close-help" class="text-gray-400 hover:text-gray-600">
            <i class="fa fa-times text-xl"></i>
          </button>
        </div>
      </div>
      <div class="p-6 space-y-4">
        <div>
          <h4 class="font-semibold text-lg mb-2">1. 选择请求方法</h4>
          <p class="text-gray-700">点击GET、POST、PUT、DELETE或PATCH按钮选择您需要的HTTP请求方法。</p>
        </div>
        <div>
          <h4 class="font-semibold text-lg mb-2">2. 输入请求URL</h4>
          <p class="text-gray-700">在URL输入框中输入您要测试的API端点URL。</p>
        </div>
        <div>
          <h4 class="font-semibold text-lg mb-2">3. 设置请求头（可选）</h4>
          <p class="text-gray-700">点击"添加"按钮添加更多请求头字段，如Content-Type、Authorization等。</p>
        </div>
        <div>
          <h4 class="font-semibold text-lg mb-2">4. 设置请求体（可选）</h4>
          <p class="text-gray-700">选择"原始数据"或"表单数据"格式，并输入请求体内容。</p>
        </div>
        <div>
          <h4 class="font-semibold text-lg mb-2">5. 高级选项（可选）</h4>
          <p class="text-gray-700">设置是否跟随重定向、是否包含凭证以及请求超时时间。</p>
        </div>
        <div>
          <h4 class="font-semibold text-lg mb-2">6. 发送请求</h4>
          <p class="text-gray-700">点击"发送请求"按钮发送请求，并在下方查看响应结果。</p>
        </div>
        <div>
          <h4 class="font-semibold text-lg mb-2">7. 查看响应</h4>
          <p class="text-gray-700">在响应区域查看状态码、响应头、响应时间和响应体。</p>
        </div>
        <div>
          <h4 class="font-semibold text-lg mb-2">8. 请求历史</h4>
          <p class="text-gray-700">所有发送的请求都会保存在历史记录中，点击可以快速重复发送。</p>
        </div>
      </div>
      <div class="p-6 border-t border-gray-200">
        <button id="close-help-btn" class="bg-primary hover:bg-primary/90 text-white px-6 py-2 rounded-lg font-medium transition-all">
          明白了
        </button>
      </div>
    </div>
  </div>

  <script>
    // 存储请求历史
    let requestHistory = [];
    let currentRequestId = null;
    
    // DOM 元素
    const methodBtns = document.querySelectorAll('.method-btn');
    const urlInput = document.getElementById('url-input');
    const sendRequestBtn = document.getElementById('send-request');
    const headersContainer = document.getElementById('headers-container');
    const addHeaderBtn = document.getElementById('add-header');
    const requestBody = document.getElementById('request-body');
    const bodyTypeBtns = document.querySelectorAll('.body-type-btn');
    const rawBodyContainer = document.getElementById('raw-body-container');
    const formBodyContainer = document.getElementById('form-body-container');
    const formBodyFields = document.getElementById('form-body-container');
    const addFormBtn = document.getElementById('add-form');
    const followRedirects = document.getElementById('follow-redirects');
    const withCredentials = document.getElementById('with-credentials');
    const timeoutInput = document.getElementById('timeout');
    const historyList = document.getElementById('history-list');
    const clearHistoryBtn = document.getElementById('clear-history');
    const responseStatus = document.getElementById('response-status');
    const responseHeaders = document.getElementById('response-headers');
    const responseTime = document.getElementById('response-time');
    const responseBody = document.getElementById('response-body');
    const responseViewBtns = document.querySelectorAll('.response-view-btn');
    const themeToggle = document.getElementById('theme-toggle');
    const helpBtn = document.getElementById('help-btn');
    const helpModal = document.getElementById('help-modal');
    const closeHelpBtn = document.getElementById('close-help');
    const closeHelpModalBtn = document.getElementById('close-help-btn');
    
    // 初始化
    init();
    
    function init() {
      // 加载历史记录
      loadHistory();
      
      // 添加事件监听器
      methodBtns.forEach(btn => {
        btn.addEventListener('click', () => selectMethod(btn));
      });
      
      sendRequestBtn.addEventListener('click', sendRequest);
      
      addHeaderBtn.addEventListener('click', addHeaderField);
      
      headersContainer.addEventListener('click', (e) => {
        if (e.target.closest('.remove-header')) {
          const headerRow = e.target.closest('.flex');
          if (headersContainer.children.length > 1) {
            headerRow.remove();
          }
        }
      });
      
      bodyTypeBtns.forEach(btn => {
        btn.addEventListener('click', () => selectBodyType(btn));
      });
      
      addFormBtn.addEventListener('click', addFormField);
      
      formBodyFields.addEventListener('click', (e) => {
        if (e.target.closest('.remove-form')) {
          const formRow = e.target.closest('.flex');
          if (formBodyFields.children.length > 1) {
            formRow.remove();
          }
        }
      });
      
      clearHistoryBtn.addEventListener('click', clearHistory);
      
      historyList.addEventListener('click', (e) => {
        const historyItem = e.target.closest('.history-item');
        if (historyItem) {
          const requestId = historyItem.dataset.id;
          loadRequestFromHistory(requestId);
        }
      });
      
      responseViewBtns.forEach(btn => {
        btn.addEventListener('click', () => selectResponseView(btn));
      });
      
      themeToggle.addEventListener('click', toggleTheme);
      
      helpBtn.addEventListener('click', () => {
        helpModal.classList.remove('hidden');
      });
      
      closeHelpBtn.addEventListener('click', () => {
        helpModal.classList.add('hidden');
      });
      
      closeHelpModalBtn.addEventListener('click', () => {
        helpModal.classList.add('hidden');
      });
      
      // 点击模态框外部关闭
      helpModal.addEventListener('click', (e) => {
        if (e.target === helpModal) {
          helpModal.classList.add('hidden');
        }
      });
      
      // ESC 键关闭模态框
      document.addEventListener('keydown', (e) => {
        if (e.key === 'Escape' && !helpModal.classList.contains('hidden')) {
          helpModal.classList.add('hidden');
        }
      });
    }
    
    // 选择HTTP方法
    function selectMethod(btn) {
      methodBtns.forEach(b => {
        b.classList.remove('bg-primary', 'text-white');
        b.classList.add('bg-white', 'text-dark', 'border', 'border-gray-300');
      });
      
      btn.classList.remove('bg-white', 'text-dark', 'border', 'border-gray-300');
      btn.classList.add('bg-primary', 'text-white');
    }
    
    // 添加请求头字段
    function addHeaderField() {
      const headerRow = document.createElement('div');
      headerRow.className = 'flex items-center gap-2';
      headerRow.innerHTML = `
        <input type="text" placeholder="Key" class="header-key flex-grow max-w-[150px] px-3 py-2 border border-gray-300 rounded-lg text-sm">
        <input type="text" placeholder="Value" class="header-value flex-grow px-3 py-2 border border-gray-300 rounded-lg text-sm">
        <button class="remove-header text-gray-400 hover:text-danger">
          <i class="fa fa-times-circle"></i>
        </button>
      `;
      headersContainer.appendChild(headerRow);
      
      // 聚焦到新添加的key输入框
      const newKeyInput = headerRow.querySelector('.header-key');
      setTimeout(() => newKeyInput.focus(), 100);
    }
    
    // 选择请求体类型
    function selectBodyType(btn) {
      bodyTypeBtns.forEach(b => {
        b.classList.remove('bg-primary/10', 'text-primary');
        b.classList.add('bg-white', 'text-gray-600');
      });
      
      btn.classList.remove('bg-white', 'text-gray-600');
      btn.classList.add('bg-primary/10', 'text-primary');
      
      const type = btn.dataset.type;
      if (type === 'raw') {
        rawBodyContainer.classList.remove('hidden');
        formBodyContainer.classList.add('hidden');
      } else {
        rawBodyContainer.classList.add('hidden');
        formBodyContainer.classList.remove('hidden');
      }
    }
    
    // 添加表单字段
    function addFormField() {
      const formRow = document.createElement('div');
      formRow.className = 'flex items-center gap-2';
      formRow.innerHTML = `
        <input type="text" placeholder="Key" class="form-key flex-grow max-w-[150px] px-3 py-2 border border-gray-300 rounded-lg text-sm">
        <input type="text" placeholder="Value" class="form-value flex-grow px-3 py-2 border border-gray-300 rounded-lg text-sm">
        <button class="remove-form text-gray-400 hover:text-danger">
          <i class="fa fa-times-circle"></i>
        </button>
      `;
      // 在"添加字段"按钮前插入新行
      addFormBtn.parentNode.insertBefore(formRow, addFormBtn);
      
      // 聚焦到新添加的key输入框
      const newKeyInput = formRow.querySelector('.form-key');
      setTimeout(() => newKeyInput.focus(), 100);
    }
    
    // 发送请求
    async function sendRequest() {
      // 获取当前选中的方法
      const method = Array.from(methodBtns).find(btn => 
        btn.classList.contains('bg-primary')
      ).dataset.method;
      
      const url = urlInput.value.trim();
      
      if (!url) {
        alert('请输入URL');
        return;
      }
      
      // 构建请求头
      const headers = {};
      const headerInputs = headersContainer.querySelectorAll('.header-key, .header-value');
      for (let i = 0; i < headerInputs.length; i += 2) {
        const key = headerInputs[i].value.trim();
        const value = headerInputs[i + 1].value.trim();
        if (key) {
          headers[key] = value;
        }
      }
      
      // 构建请求体
      let body = null;
      const activeBodyType = Array.from(bodyTypeBtns).find(btn => 
        btn.classList.contains('bg-primary/10')
      ).dataset.type;
      
      if (activeBodyType === 'raw' && requestBody.value.trim()) {
        // 尝试解析JSON
        try {
          body = JSON.parse(requestBody.value.trim());
          // 如果是JSON，确保Content-Type正确
          if (!headers['Content-Type']) {
            headers['Content-Type'] = 'application/json';
          }
        } catch (e) {
          // 不是有效的JSON，作为普通文本
          body = requestBody.value.trim();
          if (!headers['Content-Type']) {
            headers['Content-Type'] = 'text/plain';
          }
        }
      } else if (activeBodyType === 'form') {
        const formData = new FormData();
        const formInputs = formBodyFields.querySelectorAll('.form-key, .form-value');
        for (let i = 0; i < formInputs.length; i += 2) {
          const key = formInputs[i].value.trim();
          const value = formInputs[i + 1].value.trim();
          if (key) {
            formData.append(key, value);
          }
        }
        body = formData;
        // FormData不需要设置Content-Type，浏览器会自动设置
        if (headers['Content-Type'] === 'application/json') {
          delete headers['Content-Type'];
        }
      }
      
      // 配置选项
      const options = {
        method,
        headers: headers,
        credentials: withCredentials.checked ? 'include' : 'same-origin',
        redirect: followRedirects.checked ? 'follow' : 'manual'
      };
      
      // 添加请求体（非GET/HEAD请求）
      if (method !== 'GET' && method !== 'HEAD' && body) {
        options.body = typeof body === 'object' && !(body instanceof FormData) 
          ? JSON.stringify(body) 
          : body;
      }
      
      // 设置超时
      const timeout = parseInt(timeoutInput.value, 10);
      if (timeout > 0) {
        options.signal = AbortSignal.timeout(timeout);
      }
      
      // 显示加载状态
      showLoading();
      
      // 记录开始时间
      const startTime = performance.now();
      
      try {
        // 发送请求
        const response = await fetch(url, options);
        
        // 记录结束时间
        const endTime = performance.now();
        const duration = endTime - startTime;
        
        // 保存请求到历史
        saveRequestToHistory({
          method,
          url,
          headers,
          body: activeBodyType === 'raw' ? requestBody.value.trim() : getFormDataAsObject(),
          bodyType: activeBodyType,
          options: {
            followRedirects: followRedirects.checked,
            withCredentials: withCredentials.checked,
            timeout: timeout
          }
        });
        
        // 处理响应
        await processResponse(response, duration);
      } catch (error) {
        // 记录结束时间
        const endTime = performance.now();
        const duration = endTime - startTime;
        
        // 显示错误
        showError(error, duration);
      }
    }
    
    // 获取表单数据为对象
    function getFormDataAsObject() {
      const formData = {};
      const formInputs = formBodyFields.querySelectorAll('.form-key, .form-value');
      for (let i = 0; i < formInputs.length; i += 2) {
        const key = formInputs[i].value.trim();
        const value = formInputs[i + 1].value.trim();
        if (key) {
          formData[key] = value;
        }
      }
      return formData;
    }
    
    // 显示加载状态
    function showLoading() {
      // 清空之前的结果
      responseStatus.innerHTML = `
        <div class="flex flex-col justify-center items-center h-full">
          <div class="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
          <p class="mt-2 text-gray-600">正在发送请求...</p>
        </div>
      `;
      
      responseHeaders.innerHTML = `
        <div class="flex flex-col justify-center items-center h-full">
          <div class="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
          <p class="mt-2 text-gray-600">正在等待响应头...</p>
        </div>
      `;
      
      responseTime.innerHTML = `
        <div class="flex flex-col justify-center items-center h-full">
          <div class="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
          <p class="mt-2 text-gray-600">正在计算响应时间...</p>
        </div>
      `;
      
      responseBody.innerHTML = `
        <div class="flex flex-col justify-center items-center h-full">
          <div class="animate-spin rounded-full h-12 w-12 border-b-2 border-primary"></div>
          <p class="mt-2 text-gray-600">正在接收响应体...</p>
        </div>
      `;
    }
    
    // 处理响应
    async function processResponse(response, duration) {
      // 显示状态码
      const statusClass = response.ok ? 'text-secondary' : 'text-danger';
      responseStatus.innerHTML = `
        <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
          <div class="bg-white rounded-lg p-4 shadow-sm border border-gray-100">
            <p class="text-sm text-gray-500 mb-1">状态码</p>
            <p class="text-2xl font-bold ${statusClass}">${response.status}</p>
            <p class="text-sm text-gray-600">${response.statusText}</p>
          </div>
          <div class="bg-white rounded-lg p-4 shadow-sm border border-gray-100">
            <p class="text-sm text-gray-500 mb-1">响应大小</p>
            <p class="text-xl font-semibold">计算中...</p>
          </div>
          <div class="bg-white rounded-lg p-4 shadow-sm border border-gray-100">
            <p class="text-sm text-gray-500 mb-1">请求URL</p>
            <p class="text-sm font-medium truncate">${response.url}</p>
          </div>
        </div>
      `;
      
      // 显示响应时间
      responseTime.innerHTML = `
        <div class="flex flex-col justify-center items-center h-full">
          <p class="text-3xl font-bold text-primary">${duration.toFixed(2)} ms</p>
          <p class="text-gray-600 mt-1">响应时间</p>
          <div class="w-full mt-4 bg-gray-200 rounded-full h-2">
            <div class="bg-primary h-2 rounded-full" style="width: ${Math.min(duration/2000*100, 100)}%"></div>
          </div>
        </div>
      `;
      
      // 显示响应头
      let headersHtml = '<table class="min-w-full divide-y divide-gray-200">';
      headersHtml += '<thead class="bg-gray-50"><tr><th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">名称</th><th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">值</th></tr></thead>';
      headersHtml += '<tbody class="bg-white divide-y divide-gray-200">';
      
      response.headers.forEach((value, name) => {
        headersHtml += `<tr><td class="px-4 py-2 whitespace-nowrap text-sm font-medium text-gray-900">${name}</td><td class="px-4 py-2 whitespace-nowrap text-sm text-gray-500">${value}</td></tr>`;
      });
      
      headersHtml += '</tbody></table>';
      responseHeaders.innerHTML = headersHtml;
      
      // 处理响应体
      try {
        // 尝试解析为JSON
        const jsonData = await response.json();
        displayResponse(jsonData, 'json');
        
        // 更新响应大小
        const responseSize = JSON.stringify(jsonData).length;
        updateResponseSize(responseSize);
      } catch (e) {
        // 不是JSON，尝试作为文本
        try {
          const textData = await response.text();
          displayResponse(textData, 'text');
          
          // 更新响应大小
          const responseSize = textData.length;
          updateResponseSize(responseSize);
        } catch (err) {
          // 无法解析
          displayResponse('无法解析响应体', 'text');
        }
      }
    }
    
    // 更新响应大小
    function updateResponseSize(sizeInBytes) {
      let sizeText;
      if (sizeInBytes < 1024) {
        sizeText = `${sizeInBytes} B`;
      } else if (sizeInBytes < 1024 * 1024) {
        sizeText = `${(sizeInBytes / 1024).toFixed(2)} KB`;
      } else {
        sizeText = `${(sizeInBytes / (1024 * 1024)).toFixed(2)} MB`;
      }
      
      const sizeElement = responseStatus.querySelector('.bg-white:nth-child(2) p.text-xl');
      if (sizeElement) {
        sizeElement.textContent = sizeText;
      }
    }
    
    // 显示错误
    function showError(error, duration) {
      // 显示状态码
      responseStatus.innerHTML = `
        <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
          <div class="bg-white rounded-lg p-4 shadow-sm border border-gray-100">
            <p class="text-sm text-gray-500 mb-1">状态</p>
            <p class="text-2xl font-bold text-danger">错误</p>
            <p class="text-sm text-gray-600">请求失败</p>
          </div>
          <div class="bg-white rounded-lg p-4 shadow-sm border border-gray-100">
            <p class="text-sm text-gray-500 mb-1">响应时间</p>
            <p class="text-xl font-semibold">${duration.toFixed(2)} ms</p>
          </div>
          <div class="bg-white rounded-lg p-4 shadow-sm border border-gray-100">
            <p class="text-sm text-gray-500 mb-1">错误信息</p>
            <p class="text-sm font-medium text-danger truncate">${error.message}</p>
          </div>
        </div>
      `;
      
      // 显示响应时间
      responseTime.innerHTML = `
        <div class="flex flex-col justify-center items-center h-full">
          <p class="text-3xl font-bold text-danger">${duration.toFixed(2)} ms</p>
          <p class="text-gray-600 mt-1">请求超时</p>
          <div class="w-full mt-4 bg-gray-200 rounded-full h-2">
            <div class="bg-danger h-2 rounded-full" style="width: 100%"></div>
          </div>
        </div>
      `;
      
      // 显示错误信息
      responseHeaders.innerHTML = `
        <div class="flex flex-col justify-center items-center h-full text-danger">
          <i class="fa fa-exclamation-triangle text-4xl mb-2"></i>
          <p class="text-lg font-medium">请求出错</p>
          <p class="text-gray-600 mt-1">${error.message}</p>
        </div>
      `;
      
      // 清空响应体
      responseBody.innerHTML = `
        <div class="flex flex-col justify-center items-center h-full text-danger">
          <i class="fa fa-exclamation-circle text-4xl mb-2"></i>
          <p class="text-lg font-medium">无响应内容</p>
          <p class="text-gray-600 mt-1">请求未能成功获取响应</p>
        </div>
      `;
    }
    
    // 显示响应
    function displayResponse(data, type) {
      // 默认显示预览视图
      renderResponseView('preview', data, type);
    }
    
    // 选择响应视图
    function selectResponseView(btn) {
      responseViewBtns.forEach(b => {
        b.classList.remove('bg-primary/10', 'text-primary');
        b.classList.add('bg-white', 'text-gray-600');
      });
      
      btn.classList.remove('bg-white', 'text-gray-600');
      btn.classList.add('bg-primary/10', 'text-primary');
      
      const viewType = btn.dataset.view;
      renderResponseView(viewType);
    }
    
    // 渲染响应视图
    function renderResponseView(viewType, data = null, type = null) {
      // 如果没有提供数据，尝试从当前显示的数据中获取
      if (!data) {
        try {
          // 尝试从预览视图中获取数据
          if (viewType !== 'preview') {
            const previewData = JSON.parse(responseBody.querySelector('pre')?.textContent || 'null');
            if (previewData) {
              data = previewData;
              type = 'json';
            }
          }
        } catch (e) {
          // 尝试从原始视图中获取数据
          try {
            const rawData = responseBody.textContent;
            if (rawData) {
              data = rawData;
              type = 'text';
            }
          } catch (err) {
            // 无法获取数据
            return;
          }
        }
      }
      
      if (!data) {
        return;
      }
      
      switch (viewType) {
        case 'preview':
          if (type === 'json') {
            // 美化JSON显示
            try {
              const formattedJson = JSON.stringify(data, null, 2);
              responseBody.innerHTML = `<pre class="whitespace-pre-wrap font-mono text-sm bg-gray-800 text-gray-100 p-4 rounded-lg overflow-auto">${formattedJson}</pre>`;
            } catch (e) {
              responseBody.innerHTML = `<pre class="whitespace-pre-wrap font-mono text-sm p-4 rounded-lg overflow-auto">${data}</pre>`;
            }
          } else {
            // 普通文本
            responseBody.innerHTML = `<pre class="whitespace-pre-wrap font-mono text-sm p-4 rounded-lg overflow-auto">${data}</pre>`;
          }
          break;
          
        case 'raw':
          // 原始数据
          const rawData = type === 'json' ? JSON.stringify(data) : data.toString();
          responseBody.innerHTML = `<pre class="whitespace-pre-wrap font-mono text-sm p-4 rounded-lg overflow-auto">${rawData}</pre>`;
          break;
          
        case 'formatted':
          if (type === 'json') {
            // 格式化的JSON
            try {
              const formattedJson = JSON.stringify(data, null, 2);
              responseBody.innerHTML = `<pre class="whitespace-pre-wrap font-mono text-sm bg-gray-800 text-gray-100 p-4 rounded-lg overflow-auto">${formattedJson}</pre>`;
            } catch (e) {
              responseBody.innerHTML = `<pre class="whitespace-pre-wrap font-mono text-sm p-4 rounded-lg overflow-auto">${data}</pre>`;
            }
          } else {
            // 尝试格式化其他类型的数据
            responseBody.innerHTML = `<pre class="whitespace-pre-wrap font-mono text-sm p-4 rounded-lg overflow-auto">${data}</pre>`;
          }
          break;
      }
    }
    
    // 保存请求到历史
    function saveRequestToHistory(request) {
      const timestamp = new Date().toISOString();
      const requestId = `req_${timestamp.replace(/[:.]/g, '_')}`;
      
      const historyItem = {
        id: requestId,
        timestamp,
        ...request
      };
      
      requestHistory.unshift(historyItem);
      
      // 最多保存20条历史记录
      if (requestHistory.length > 20) {
        requestHistory.pop();
      }
      
      // 保存到localStorage
      localStorage.setItem('requestHistory', JSON.stringify(requestHistory));
      
      // 更新历史列表UI
      updateHistoryList();
      
      // 高亮当前请求
      currentRequestId = requestId;
    }
    
    // 加载历史记录
    function loadHistory() {
      const savedHistory = localStorage.getItem('requestHistory');
      if (savedHistory) {
        requestHistory = JSON.parse(savedHistory);
        updateHistoryList();
      }
    }
    
    // 更新历史列表UI
    function updateHistoryList() {
      if (requestHistory.length === 0) {
        historyList.innerHTML = `<div class="text-center text-gray-500 text-sm italic py-4">暂无请求历史</div>`;
        return;
      }
      
      historyList.innerHTML = '';
      
      requestHistory.forEach(item => {
        const isCurrent = item.id === currentRequestId;
        const timestamp = new Date(item.timestamp).toLocaleString();
        
        const historyItem = document.createElement('div');
        historyItem.className = `history-item p-3 rounded-lg flex items-center justify-between cursor-pointer ${isCurrent ? 'bg-primary/10 border-l-4 border-primary' : 'hover:bg-gray-50'}`;
        historyItem.dataset.id = item.id;
        historyItem.innerHTML = `
          <div class="flex items-center gap-3">
            <span class="px-3 py-1 rounded text-xs font-medium ${getMethodColorClass(item.method)}">${item.method}</span>
            <div>
              <p class="text-sm font-medium ${isCurrent ? 'text-primary' : 'text-dark'} truncate max-w-[250px]">${item.url}</p>
              <p class="text-xs text-gray-500">${timestamp}</p>
            </div>
          </div>
          <button class="text-gray-400 hover:text-danger delete-history" data-id="${item.id}">
            <i class="fa fa-trash-o"></i>
          </button>
        `;
        
        historyList.appendChild(historyItem);
      });
      
      // 添加删除历史记录事件
      document.querySelectorAll('.delete-history').forEach(btn => {
        btn.addEventListener('click', (e) => {
          e.stopPropagation(); // 防止触发加载请求
          const requestId = btn.dataset.id;
          deleteHistoryItem(requestId);
        });
      });
    }
    
    // 获取HTTP方法的颜色类
    function getMethodColorClass(method) {
      switch (method.toUpperCase()) {
        case 'GET':
          return 'bg-blue-100 text-blue-800';
        case 'POST':
          return 'bg-green-100 text-green-800';
        case 'PUT':
          return 'bg-yellow-100 text-yellow-800';
        case 'DELETE':
          return 'bg-red-100 text-red-800';
        case 'PATCH':
          return 'bg-purple-100 text-purple-800';
        default:
          return 'bg-gray-100 text-gray-800';
      }
    }
    
    // 从历史记录中加载请求
    function loadRequestFromHistory(requestId) {
      const request = requestHistory.find(item => item.id === requestId);
      if (!request) {
        return;
      }
      
      // 设置URL
      urlInput.value = request.url;
      
      // 设置请求方法
      methodBtns.forEach(btn => {
        if (btn.dataset.method === request.method) {
          selectMethod(btn);
        }
      });
      
      // 设置请求头
      headersContainer.innerHTML = '';
      if (request.headers) {
        Object.entries(request.headers).forEach(([key, value]) => {
          const headerRow = document.createElement('div');
          headerRow.className = 'flex items-center gap-2';
          headerRow.innerHTML = `
            <input type="text" placeholder="Key" value="${key}" class="header-key flex-grow max-w-[150px] px-3 py-2 border border-gray-300 rounded-lg text-sm">
            <input type="text" placeholder="Value" value="${value}" class="header-value flex-grow px-3 py-2 border border-gray-300 rounded-lg text-sm">
            <button class="remove-header text-gray-400 hover:text-danger">
              <i class="fa fa-times-circle"></i>
            </button>
          `;
          headersContainer.appendChild(headerRow);
        });
      } else {
        // 添加默认Content-Type头
        addHeaderField();
        headersContainer.querySelector('.header-key').value = 'Content-Type';
        headersContainer.querySelector('.header-value').value = 'application/json';
      }
      
      // 设置请求体
      if (request.bodyType === 'raw') {
        bodyTypeBtns.forEach(btn => {
          if (btn.dataset.type === 'raw') {
            selectBodyType(btn);
          }
        });
        requestBody.value = request.body || '';
      } else {
        bodyTypeBtns.forEach(btn => {
          if (btn.dataset.type === 'form') {
            selectBodyType(btn);
          }
        });
        
        formBodyFields.innerHTML = '';
        if (request.body && typeof request.body === 'object') {
          Object.entries(request.body).forEach(([key, value]) => {
            const formRow = document.createElement('div');
            formRow.className = 'flex items-center gap-2';
            formRow.innerHTML = `
              <input type="text" placeholder="Key" value="${key}" class="form-key flex-grow max-w-[150px] px-3 py-2 border border-gray-300 rounded-lg text-sm">
              <input type="text" placeholder="Value" value="${value}" class="form-value flex-grow px-3 py-2 border border-gray-300 rounded-lg text-sm">
              <button class="remove-form text-gray-400 hover:text-danger">
                <i class="fa fa-times-circle"></i>
              </button>
            `;
            formBodyFields.appendChild(formRow);
          });
        }
      }
      
      // 设置选项
      followRedirects.checked = request.options?.followRedirects || false;
      withCredentials.checked = request.options?.withCredentials || false;
      timeoutInput.value = request.options?.timeout || 0;
      
      // 高亮当前请求
      currentRequestId = requestId;
      updateHistoryList();
    }
    
    // 删除单条历史记录
    function deleteHistoryItem(requestId) {
      requestHistory = requestHistory.filter(item => item.id !== requestId);
      localStorage.setItem('requestHistory', JSON.stringify(requestHistory));
      
      if (currentRequestId === requestId) {
        currentRequestId = null;
      }
      
      updateHistoryList();
    }
    
    // 清空历史记录
    function clearHistory() {
      requestHistory = [];
      localStorage.removeItem('requestHistory');
      currentRequestId = null;
      updateHistoryList();
    }
    
    // 切换主题（预留功能）
    function toggleTheme() {
      const icon = themeToggle.querySelector('i');
      if (icon.classList.contains('fa-moon-o')) {
        icon.classList.remove('fa-moon-o');
        icon.classList.add('fa-sun-o');
        // 这里可以添加深色模式的实现
      } else {
        icon.classList.remove('fa-sun-o');
        icon.classList.add('fa-moon-o');
        // 这里可以添加浅色模式的实现
      }
    }
  </script>
</body>
</html>
  